package com.after.saas.file.aop;

import com.after.saas.common.annotation.Log;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

/**
 * @Classname PrintAspect
 * @Description TODO
 * @auth after
 * @since 2023/6/18 18:04
 */

@Aspect
@Component
public class PrintAspect {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 在使用spring框架配置AOP的时候，不管是通过XML配置文件还是注解的方式都需要定义pointcut"切入点"
     * 例如定义切入点表达式 `execution(* com.sample.service.impl..*.*(..))` execution()是最常用的切点函数，其语法如下所示：整个表达式可以分为五个部分：
     * 1. execution(): 表达式主体。
     * 2. 第一个*号：表示返回类型，*号表示所有的类型。
     * 3. 包名：表示需要拦截的包名，后面的两个句点表示当前包和当前包的所有子包，com.sample.service.impl包、子孙包下所有类的方法。
     * 4. 第二个*号：表示类名，*号表示所有的类。
     * 5. *(..):最后这个星号表示方法名，*号表示所有的方法，后面括弧里面表示方法的参数，两个句点表示任何参数。
     */
    @Pointcut("execution(* com.after.saas.file.service.EasyExcelService.read*(..))")
    public void log() {
    }

    /**
     * 前置通知：在被代理方法之前调用
     */
    @Before("log()")
    public void doBefore() {
        logger.warn("调用方法之前：");
        logger.warn("接收到请求！");
    }

    /**
     * 后置通知：在被代理方法之后调用
     */
    @After("log()")
    public void doAfter() {
        logger.warn("调用方法之后：");
        logger.warn("打印请求内容完成！");
    }

    /**
     * 返回通知：被代理方法正常返回之后调用
     */
    @Async
    @AfterReturning(pointcut = "log()", returning = "retVal")
    public void doReturning(JoinPoint pjp, Object retVal) {
        logger.warn("方法正常返回之后：");
        logger.info("method {},args {}", pjp.getSignature().getName(), pjp.getArgs());
        logger.warn("完成返回内容！retVal \n{}",retVal);
        int n=0; int num=3;
        int r=num/n;
    }

    /**
     * 异常通知：被代理方法抛出异常时调用
     */
    @AfterThrowing("log()")
    public void doThrowing() {
        logger.error("方法抛出异常！");
    }

    /**
     * 环绕通知
     */
    @Around("log()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        logger.info("method {},args {}", pjp.getSignature().getName(), pjp.getArgs());
        logger.info("----------around before----------");
        Object result = pjp.proceed();
        logger.info("----------around post----------");
        logger.info("result \n{}", result);
        return result;
    }
}
